#include "logger.h"

static TCHAR const *logfile_name_;
static unsigned __int64 logfile_limit_;

static logsubscriber *logfns_;
static int logfns_size_;



int 
log_output(TCHAR const *category, TCHAR const *format, ...) {
	va_list arglist;
	TCHAR msg[MAX_PATH];
	TCHAR *pmsg;
	int cnt;
	int size = MAX_PATH;
	int idx;

	/* 
	 * Validate arguments 
	 */
	if (0 == logfns_ || 0 == logfns_size_ || 0 == format) {
		return (FALSE);
	}

	/*
	 * Format category argument
	 */
	pmsg = &msg[0];
	if (0 != category) {
		cnt = _stprintf_s(pmsg, size, _T("[%s]: "), category);
		if (-1 == cnt) {
			return (FALSE);
		}

		pmsg += cnt;
		size = MAX_PATH - cnt;
	} 

	/*
	 * Format va_list arguments.
	 */
	va_start(arglist, format);
	cnt = _vstprintf_s(pmsg, size, format, arglist);
	va_end(arglist);
	if (-1 == cnt) {
		return (FALSE);
	}

	/*
	 * Dispatch log subscribers.
	 */
	for (idx = 0; idx < logfns_size_; ++idx) {
		cnt += logfns_[idx](msg);
	}

	return (cnt);
}

void 
clean_log_subscribers(void) {
	free(logfns_);
}

DWORD WINAPI 
subscribe_dbglog(TCHAR const *log) {
	if (0 == log) {
		return (FALSE);
	}

	OutputDebugString(log);

	return (TRUE);
}

DWORD WINAPI 
subscribe_filelog(TCHAR const *log) {
	FILE *f;
	errno_t e;
	int cnt;
	WIN32_FIND_DATA file;
	HANDLE h;
	DWORD lasterr;
	int found = 1;
	unsigned __int64 fsize = 0;
	CRITICAL_SECTION lock;


	if (0 == logfile_name_) {
		return (FALSE);
	}

	h = FindFirstFile(logfile_name_, &file);
	if (INVALID_HANDLE_VALUE == h) {
		lasterr = GetLastError();
		if (ERROR_FILE_NOT_FOUND == lasterr || 
			ERROR_PATH_NOT_FOUND == lasterr || 
			ERROR_INVALID_DRIVE == lasterr) {
				found = 0;
		} else {
			return (FALSE);
		}
	}
	FindClose(h);

	InitializeCriticalSection(&lock);
	EnterCriticalSection(&lock);

	if (found && (0 == (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))) {
		fsize = ((unsigned __int64)file.nFileSizeHigh << 0x20) | (file.nFileSizeLow & 0xffffffffL);
		if (fsize >= logfile_limit_) {
			if (0 == DeleteFile(logfile_name_)) {
				return (FALSE);
			}
			fsize = 0;
		} else if (FILE_ATTRIBUTE_ARCHIVE != (file.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)) {
			if (0 == SetFileAttributes(logfile_name_, FILE_ATTRIBUTE_ARCHIVE)) {
				return (FALSE);
			}
		}
	}

	e = _tfopen_s(&f, logfile_name_, _T("a+"));
	if (0 != e) {
		cnt = FALSE;
		goto RELEASE_LOCK;
	}

	if (0 == fsize) {
		cnt = _ftprintf_s(f, _T("%s"), log);
	} else {
		cnt = _ftprintf_s(f, _T("\n%s"), log);
	}
	fclose(f);

RELEASE_LOCK:
	LeaveCriticalSection(&lock);
	DeleteCriticalSection(&lock);

	return (cnt);
}

int 
config_filelog_subscriber(TCHAR const *logfile_name, unsigned __int64 logfile_limit) {
	if (0 == logfile_name) {
		return (FALSE);
	}

	logfile_name_ = logfile_name;
	logfile_limit_ = logfile_limit;

	return (TRUE);
}

int 
config_log_subscribers(int subscribers_size, ...) {
	va_list arglist;
	logsubscriber *p;
	int e;

	if (subscribers_size < 1) {
		return (FALSE);
	}

	logfns_ = (logsubscriber*)calloc(subscribers_size, sizeof(logsubscriber));
	if (0 == logfns_ || 0 != _get_errno(&e) || ENOMEM == e) {
		return (FALSE);
	} 
	logfns_size_ = subscribers_size;

	p = logfns_;
	va_start(arglist, subscribers_size);
	while (subscribers_size--) {
		*p++ = va_arg(arglist, logsubscriber);
	}
	va_end(arglist);
	
	return (TRUE);
}




